package com.suhuamo.mybatis;

import com.suhuamo.Student;
import com.suhuamo.StudentMapper;
import sun.reflect.generics.reflectiveObjects.ParameterizedTypeImpl;

import java.lang.reflect.*;
import java.sql.*;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

/**
 * @author yuanchuncheng
 * @slogan 今天的早餐是：早苗的面包、秋子的果酱和观铃的果汁~
 * @date 2023-10-31
 * @description mapper接口代理工厂
 */
public class MapperProxyFactory {
    // 类型处理器
    public static Map<String, TypeHandler> typeHandlerMap = new HashMap<>();
    static {
        try {
            // 加载驱动
            Class.forName("com.mysql.cj.jdbc.Driver");
        } catch (ClassNotFoundException e) {
            throw new RuntimeException(e);
        }
        // 加载类型处理器
        typeHandlerMap.put(Integer.class.toString(), new IntegerHandler());
        typeHandlerMap.put(String.class.toString(), new StringHandler());
    }

    public static <T> T getMapper(Class<T> mapper) {
        // 1. 创建动态代理类
       return (T)Proxy.newProxyInstance(ClassLoader.getSystemClassLoader(), new Class[]{mapper}, new InvocationHandler() {
            @Override
            public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
                // 1. 创建链接
                Connection connection = DriverManager.getConnection("jdbc:mysql://localhost:3306/temp_ycc", "root", "");
                // 2. 获取用户输入的sql语句
                Select annotation = method.getAnnotation(Select.class);
                String sql = annotation.value();
                // 3. 将sql语句处理，转换为预编译的sql语句
                String prepareSql = parseSql("#{","}","?", sql);
                HashMap<Integer, String> prepareParamMapping = parseSqlGetParamMapping("#{", "}", sql);
                HashMap<String, Object> argParamMapping = new HashMap<>();
                // 4. 创建 PreparedStatement，执行sql语句
                PreparedStatement statement = connection.prepareStatement(prepareSql);
                // 填充参数
                Parameter[] parameters = method.getParameters();
                for (int i = 0; i < parameters.length; i++) {
                    Param param = parameters[i].getAnnotation(Param.class);
                    // 不为空，则按照定义的名称赋值
                    if(param != null) {
                        argParamMapping.put(param.value(), args[i]);
                        // 未写Param，则按照变量名赋值
                    } else {
                        // jdk1.8之前获取的是 arg0, 1.8做了配置之后，才能获取到变量名
                        argParamMapping.put(parameters[i].getName(), args[i]);
                    }
                }
                // 填充参数
                prepareParamMapping.forEach((idx, name) -> {
                    Object o = argParamMapping.get(name);
                    TypeHandler typeHandler = typeHandlerMap.get(o.getClass().toString());
                    try {
                        typeHandler.setParameter(statement, idx + 1, o);
                    } catch (SQLException e) {
                        throw new RuntimeException(e);
                    }
                });
                // 4. 执行查询
                ResultSet resultSet = statement.executeQuery();
                // 返回结果
                Object result;
                // 从数据库中获取返回对象
                List<Object> objectList = new ArrayList<>();
                // 解析实体类的数据类型
                Type genericReturnType = method.getGenericReturnType();
                Class returnType;
                // 如果是单个Class对象，则直接取类型
                if(genericReturnType instanceof Class) {
                    returnType = (Class) genericReturnType;
                    // 如果是泛型，则取第一个对象的类型
                } else {
                    Type[] actualTypeArguments = ((ParameterizedType) genericReturnType).getActualTypeArguments();
                    returnType = (Class) actualTypeArguments[0];
                }
                 Map<String, Method> methodMap = new HashMap<>();
                // 获取赋值方法
                Method[] declaredMethods = returnType.getDeclaredMethods();
                for (int i = 0; i < declaredMethods.length; i++) {
                    Method declaredMethod = declaredMethods[i];
                    if(declaredMethod.getName().startsWith("set")) {
                        // 去掉 set 这三个字符，并且首字母小写
                        String name = declaredMethod.getName().substring(3, 4).toLowerCase() + declaredMethod.getName().substring(4);
                        methodMap.put(name, declaredMethod);
                    }
                }
                // 5. 数据转换
                while (resultSet.next()) {
                    Object o = returnType.newInstance();
                    // 遍历该对象的所有set方法，将得到的结果集字段通过set方法注入到对象中
                    methodMap.forEach((k, v) -> {
                        try {
                            // 获取该方法的变量的数据类型
                            Class<?> parameterType = v.getParameterTypes()[0];
                            TypeHandler typeHandler = typeHandlerMap.get(parameterType.toString());
                            v.invoke(o, typeHandler.getResult(resultSet, k));
                        } catch (IllegalAccessException e) {
                            throw new RuntimeException(e);
                        } catch (InvocationTargetException | SQLException e) {
                            throw new RuntimeException(e);
                        }
                    });
                    objectList.add(o);
                }
                // 6. 关闭链接
                connection.close();
                // 7. 获取返回对象的类型
                // 如果是List，则返回List
                if(method.getReturnType().equals(List.class)) {
                    result = objectList;
                    // 否则是单个Class对象，返回第一个对象
                } else {
                    result = objectList.get(0);
                }
                // 7. 返回结果
                return result;
            }
        });
    }

    /**
     * 解析target字符串，将 beginStr 和 endStr 之间的字符串替换为 replacement，并返回新的字符串
     * @param beginStr
     * @param endStr
     * @param replacement
     * @param target
     * @return
     */
    private static  String parseSql(String beginStr, String endStr, String replacement, String target) {
        StringBuffer stringBuffer = new StringBuffer();
        for (int i = 0; i < target.length(); i++) {
            // 如果未匹配，则该字符可以使用 
            if(target.charAt(i) != beginStr.charAt(0)) {
                stringBuffer.append(target.charAt(i));
                continue;
            }
            // p 用于指针移动
            int p = i;
            boolean beginCompareFlag = true;
            // 如果匹配上了前缀，则再判断是否需要跳过这段匹配
            for(int j = 0; j < beginStr.length(); j++) {
                // 如果有一个字段未匹配上，则未匹配
                if(target.charAt(p++) != beginStr.charAt(j)) {
                    beginCompareFlag = false;
                    break;
                }
            }
            // 前缀未匹配成功
            if(!beginCompareFlag) {
                continue;
            }
            // 匹配后缀
            boolean endCompareFlag = true;
            int q = p;
            for(int j = 0; j < endStr.length(); j++) {
                // 防止输入错误造成死循环,p >= target.length() 也为终止条件
                if(q >= target.length()) {
                    endCompareFlag = false;
                    break;
                }
                // 如果有一个字段未匹配上，则未匹配,同时刷新后缀的指针位置
                if(target.charAt(q++) != endStr.charAt(j)) {
                    j = -1;
                    continue;
                }
            }
            // 后缀未匹配成功
            if(!endCompareFlag) {
                continue;
            }
            // 前缀和后缀都匹配成功了
            i = q - 1;
            stringBuffer.append(replacement);
        }
        return stringBuffer.toString();
    }

    private static  HashMap<Integer,String> parseSqlGetParamMapping(String beginStr, String endStr, String target) {
        HashMap<Integer, String> prepareParamMapping = new HashMap<>();
        int count = 0;
        for (int i = 0; i < target.length(); i++) {
            // 如果未匹配，则该字符可以使用
            if(target.charAt(i) != beginStr.charAt(0)) {
                continue;
            }
            // p 用于指针移动
            int p = i;
            boolean beginCompareFlag = true;
            // 如果匹配上了前缀，则再判断是否需要跳过这段匹配
            for(int j = 0; j < beginStr.length(); j++) {
                // 如果有一个字段未匹配上，则未匹配
                if(target.charAt(p++) != beginStr.charAt(j)) {
                    beginCompareFlag = false;
                    break;
                }
            }
            // 前缀未匹配成功
            if(!beginCompareFlag) {
                continue;
            }
            // 匹配后缀
            boolean endCompareFlag = true;
            int q = p;
            for(int j = 0; j < endStr.length(); j++) {
                // 防止输入错误造成死循环,p >= target.length() 也为终止条件
                if(q >= target.length()) {
                    endCompareFlag = false;
                    break;
                }
                // 如果有一个字段未匹配上，则未匹配,同时刷新后缀的指针位置
                if(target.charAt(q++) != endStr.charAt(j)) {
                    j = -1;
                    continue;
                }
            }
            // 后缀未匹配成功
            if(!endCompareFlag) {
                continue;
            }
            // 前缀和后缀都匹配成功了
            i = q - 1;
            prepareParamMapping.put(count++, target.substring(p, q - endStr.length()));
        }
        return prepareParamMapping;
    }
}
